package com.jsg.sl651.codec.impl;

import com.jsg.sl651.codec.FrameDecoder;
import com.jsg.sl651.entity.frame.FrameMessageWrapper;
import com.jsg.sl651.entity.frame.body.MessageBody;
import com.jsg.sl651.entity.frame.body.MessageBodyFactory;
import com.jsg.sl651.entity.frame.body.returns.PackageReplacementResponse;
import com.jsg.sl651.entity.frame.header.MessageHeader;
import com.jsg.sl651.enums.CommunicationMessageType;
import com.jsg.sl651.enums.ControlCharacterDefinition;
import com.jsg.sl651.enums.server.ServerCodeType;
import com.jsg.sl651.manager.SubcontractingMessageManager;
import com.jsg.sl651.util.ByteUtil;
import com.jsg.sl651.util.CopyUtil;
import com.jsg.sl651.util.SL651Tool;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;

/**
 * 报文解码实例
 */
@AllArgsConstructor
@Slf4j
public class FrameDecoderImpl implements FrameDecoder {

    private final int minLength;
    private final int maxLength;
    private final ServerCodeType serverCodeType;


    /**
     * 解码报文
     * @param data 报文
     * @param ctx
     * @return 解码后的报文
     */
    @Override
    public FrameMessageWrapper decode(ByteBuf data, ChannelHandlerContext ctx) {
        try {
            // 先读取报文开始帧
            FrameMessageWrapper frameMessageWrapper = initFrame(data, ctx);
            if (frameMessageWrapper == null) {
                return null;
            }
            // 检查当前模式下是否支持该类型的报文
            if (!checkServerCodeType(frameMessageWrapper.getMessageType())) {
                log.error("当前服务器不支持该类型的报文，请检查报文类型。当前报文类型：{}，服务器支持类型：{}", frameMessageWrapper.getMessageType().getType(), serverCodeType.getType());
                release(data, ctx);
                return null;
            }
            frameMessageWrapper.setHeader(MessageHeader.create(frameMessageWrapper.getMessageType(), data));
            // 读取完报文正文
            byte[] text = ByteUtil.readBytes(data, frameMessageWrapper.getHeader().getIdentificationAndLength().getLength());
            frameMessageWrapper.setBodyData(text);
            // 读取完报文结束帧
            byte b = data.readByte();
            frameMessageWrapper.setEndOfTextSymbol(ControlCharacterDefinition.getControlCharacterDefinition(frameMessageWrapper.getMessageType(), ByteUtil.toHex(b)));
            // 读取两个字节的无符号CRC
            byte[] bytes = ByteUtil.readBytes(data, 2);
            frameMessageWrapper.setCrc(bytes[0] << 8 | bytes[1]);
            if (frameMessageWrapper.getHeader().isM3()) {
                return handlerM3(frameMessageWrapper, ctx);
            } else {
                MessageBody decode = MessageBodyFactory.decode(frameMessageWrapper.getMessageType(), frameMessageWrapper.getHeader().getFunction(), frameMessageWrapper.getBodyData());
                frameMessageWrapper.setBody(decode);
                return frameMessageWrapper;
            }
        } catch (Exception e) {
            ReferenceCountUtil.safeRelease(data);
            e.printStackTrace();
            return null;
        }
    }

    private FrameMessageWrapper handlerM3(FrameMessageWrapper msg, ChannelHandlerContext ctx) throws Exception {
        SubcontractingMessageManager.addMessage(msg.getHeader().getRemoteTelemetryStationAddress().getMessageHex(),
                msg.getHeader().getFunction().getCode(), msg.getHeader().getPacketInfo(), msg.getBodyData());
        // 报文还没有传输完整
        if (msg.getHeader().getEndOfTextSymbol() == ControlCharacterDefinition.ETB) {
            return null;
        }
        // 报文提示已经传输完整了
        if (SubcontractingMessageManager.validMessage(msg.getHeader().getRemoteTelemetryStationAddress().getMessageHex(),msg.getHeader().getFunction().getCode())) {
            // 确认报文已经传输完整了
            byte[] message = SubcontractingMessageManager.getMessage(msg.getHeader().getRemoteTelemetryStationAddress().getMessageHex(), msg.getHeader().getFunction().getCode());
            msg.setBodyData(message);
            MessageBody decode = MessageBodyFactory.decode(msg.getMessageType(), msg.getHeader().getFunction(), msg.getBodyData());
            msg.setBody(decode);
            return msg;
        } else {
            // 虽然提示了报文已经传输完整了，但是还有包没有传输完，所以需要要求重发报文
            int[] missingNumbers = SubcontractingMessageManager.getMissingPackerNumbers(msg.getHeader().getRemoteTelemetryStationAddress().getMessageHex(), msg.getHeader().getFunction().getCode());
            if (missingNumbers == null || missingNumbers.length == 0) {
                log.warn("当前检查报文包数量一致，将重新执行方法");
                return handlerM3(msg, ctx);
            } else {
                // todo 发送一个要求重发的报文 【这个留着 需要后面的代码实现】
                PackageReplacementResponse response = new PackageReplacementResponse(msg.getBody().getFlowNumber());
                FrameMessageWrapper frameMessageWrapper = new FrameMessageWrapper().setMessageType(msg.getMessageType()).setBody(response);
                // 设置反馈报文头信息，设置需要重发报文包号
                MessageHeader messageHeader = CopyUtil.copyHeader(msg.getHeader());
                messageHeader.getPacketInfo().setPacketNumber(missingNumbers[0]);
                // 反馈重发
                frameMessageWrapper.setHeader(messageHeader).setEndOfTextSymbol(ControlCharacterDefinition.NAK);
                // 写回响应数据
                ctx.writeAndFlush(frameMessageWrapper).addListener(future -> {
                    if (!future.isSuccess()) {
                        log.error("[发送失败]", future.cause());
                    }
                });
                return null;
            }
        }

    }

    private boolean checkServerCodeType(CommunicationMessageType messageType) {
        if (serverCodeType == ServerCodeType.BOTH) {
            return true;
        } else {
            return messageType.getType().equalsIgnoreCase(serverCodeType.getType());
        }
    }

    public FrameMessageWrapper initFrame(ByteBuf data, ChannelHandlerContext ctx) {
        // 先读取报文开始帧
        int readableBytes = data.readableBytes();
        if (readableBytes < minLength || readableBytes > maxLength) {
            log.error("报文长度错误，最小长度为{}，最大长度为{}，实际长度为{}", minLength, maxLength, readableBytes);
            release(data, ctx);
            return null;
        }
        if (!SL651Tool.validSl651Crc(data)) {
            log.error("报文校验失败，具体报文HEX:\r\n {}", ByteBufUtil.hexDump(data));
            release(data, ctx);
            return null;
        }
        FrameMessageWrapper frameMessageWrapper = new FrameMessageWrapper();
        // 读取开始字节
        byte start = data.readByte();
        if (ControlCharacterDefinition.SOH.getHex().startsWith(ByteUtil.toHex(start))) {
            frameMessageWrapper.setMessageType(CommunicationMessageType.HEX);
            // hex 模式下是两个字节
            data.readByte();
        } else {
            frameMessageWrapper.setMessageType(CommunicationMessageType.ASCII);
        }
        return frameMessageWrapper;
    }





    private static void release(ByteBuf data, ChannelHandlerContext ctx) {
        data.clear();
        ctx.channel().close();
    }



}
